/*
 * SPDX-FileCopyrightText: 2023-2023 liulinsong <liulinsong@kylinos.cn>
 *
 * SPDX-License-Identifier: LGPL-2.1-or-later
 *
 */
#ifndef _FCITX_MODULES_DEVICESTATUSMANAGER_STATUSMANAGERWATCHER_H_
#define _FCITX_MODULES_DEVICESTATUSMANAGER_STATUSMANAGERWATCHER_H_

#include <functional>
#include <memory>
#include <string>

#include "fcitx-utils/dbus/servicewatcher.h"
#include "fcitx/addonmanager.h"
#include "fcitx/instance.h"
#include "dbus_public.h"

namespace fcitx {
class StatusManagerWatcher {
public:
    class Strategy;

public:
    StatusManagerWatcher(std::unique_ptr<Strategy> strategy,
                         Instance *instance);
    ~StatusManagerWatcher();

    bool isSubjectAvailable() const { return isSubjectAvailable_; }

private:
    void updateSubjectDefaultAvailable();
    void watch();
    void connectSignal();

    bool isSubjectAvailableFromSignalMessage(dbus::Message &message) const;

    void setSubjectAvailable(bool available);
    void updateSubjectAvailable(bool available);
    bool isSubjectAvailableDBus();

private:
    using MethodCallback = std::function<void(dbus::Message &)>;

private:
    void callDBusMethod(const std::string &methodName,
                        const MethodCallback &methodCallback);

private:
    FCITX_ADDON_DEPENDENCY_LOADER(dbus, instance_->addonManager());

    std::unique_ptr<Strategy> strategy_;

    bool isSubjectAvailable_;

    Instance *instance_ = nullptr;
    dbus::Bus *bus_ = nullptr;
    dbus::ServiceWatcher watcher_;
    std::unique_ptr<dbus::ServiceWatcherEntry> entry_;
    std::unique_ptr<dbus::Slot> slot_;
};

class StatusManagerWatcher::Strategy {
public:
    using SubjectAvailabilityChangedCallback = std::function<void()>;

public:
    virtual ~Strategy() = default;

    void nofityDeviceAvailabilityChanged() {
        if (!availabilityChangedCallback_) {
            return;
        }

        availabilityChangedCallback_();
    }

    const std::string &getName() const { return name_; }
    const std::string &getDbusSignalName() const { return dbusSignalName_; }
    const std::string &getDbusMethodName() const { return dbusMethodName_; }

    virtual bool isSubjectDefaultAvailable() const = 0;
    virtual bool
    isSubjectAvailableFromMethodMessage(dbus::Message &message) const = 0;

protected:
    Strategy(const std::string &name, const std::string &dbusSignalName,
             const std::string &dbusMethodName,
             SubjectAvailabilityChangedCallback availabilityChangedCallback)
        : name_(name), dbusSignalName_(dbusSignalName),
          dbusMethodName_(dbusMethodName),
          availabilityChangedCallback_(std::move(availabilityChangedCallback)) {
    }

private:
    const std::string name_;
    const std::string dbusSignalName_;
    const std::string dbusMethodName_;

    SubjectAvailabilityChangedCallback availabilityChangedCallback_;
};
} // namespace fcitx

#endif